﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Wolfpack.Contrib.DashboardAdapter.Interfaces;
using Wolfpack.Core;
using Wolfpack.Core.Interfaces.Entities;
using Wolfpack.Core.Publishers;

namespace Wolfpack.Contrib.DashboardAdapter.Periscope.Formatters
{
    public class PeriscopeTemplatePublisher : FilteredResultPublisherBase<PeriscopeTemplatePublisherConfig>
    {
        private int _bufferSize;
        private readonly ConcurrentQueue<NotificationEvent> _notificationTracker; 
        private readonly ITemplateRespository _templateRepository;
        private readonly IMessageFormatter _formatter;

        public PeriscopeTemplatePublisher(PeriscopeTemplatePublisherConfig config,
            ITemplateRespository templateRepository,
            IMessageFormatter formatter) 
            : base(config, config.SourceCheckIdToPush)
        {
            _templateRepository = templateRepository;
            _formatter = formatter;

            _bufferSize = 0;
            _notificationTracker = new ConcurrentQueue<NotificationEvent>();
        }

        public PeriscopeTemplatePublisher(PeriscopeTemplatePublisherConfig config,
            Func<NotificationEvent, bool> filter,
            ITemplateRespository templateRepository,
            IMessageFormatter formatter)
            : base(config, filter)
        {
            _templateRepository = templateRepository;
            _formatter = formatter;
        }

        public override void Initialise()
        {
            if (BufferEnabled() && (Config.NumberOfEventsToPush > Config.NumberOfEventsToTrack))
                throw new InvalidOperationException(
                    string.Format("Periscope Template Publisher '{0}' is incorrectly configured; NumberOfEventsToPush cannot be greater than NumberOfEventsToTrack", 
                    Config.FriendlyId));
        }

        protected override void Publish(NotificationEvent notification)
        {
            try
            {
                var additionalData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

                if (Config.NumberOfEventsToTrack > 1)
                {
                    _bufferSize++;
                    _notificationTracker.Enqueue(notification);

                    while (_notificationTracker.Count > Config.NumberOfEventsToTrack)
                    {
                        NotificationEvent removed;
                        _notificationTracker.TryDequeue(out removed);
                    }

                    if (BufferEnabled())
                    {
                        if (!BufferFull())
                        {
                            return;
                        }
                    }

                    var snapshot = _notificationTracker.ToArray().OrderBy(x => x.GeneratedOnUtc);
                    additionalData["X-Series"] = GenerateXSeries(snapshot);
                    additionalData["Y-Series"] = GenerateYSeries(snapshot);
                }

                var template = _templateRepository.Load(GetTemplateName());
                var output = _formatter.Format(template, notification, additionalData);

                Logger.Debug("Pushing Periscope template for Notification '{0}' to widget '{1}'...",
                    Config.SourceCheckIdToPush, Config.TargetWidgetId);
                Messenger.Publish(new WidgetUpdateRequest
                {
                    Payload = output,
                    TargetWidgetId = Config.TargetWidgetId
                });

                BufferReset();
            }
            catch (Exception e)
            {
                Logger.Error(Logger.Event.During("PeriscopeTemplatePublisher.Publish()")
                    .Encountered(e));
            }
        }

        private void BufferReset()
        {
            _bufferSize = 0;
        }

        private bool BufferFull()
        {
            return _bufferSize >= Config.NumberOfEventsToPush;
        }

        private bool BufferEnabled()
        {
            return Config.NumberOfEventsToPush > 1;
        }

        protected virtual string GenerateXSeries(IEnumerable<NotificationEvent> events)
        {
            // default to date/time labels
            return string.Join(",", events.Select(x =>
                {
                    if (x.GeneratedOnUtc.Date == DateTime.Today)
                        return x.GeneratedOnUtc.ToString("H:mm:ss");
                    // add other formats varied by separation from "now" here...
                    return x.GeneratedOnUtc.ToString("d-MMM H:mm:ss");
                })
                .Select(x => string.Format("\"{0}\"", x)));
        }
        protected virtual string GenerateYSeries(IEnumerable<NotificationEvent> events)
        {
            // default to ResultCount values
            return string.Join(",", events.Select(x => x.ResultCount.GetValueOrDefault(0)));
        }

        protected virtual string GetTemplateName()
        {
            return string.IsNullOrWhiteSpace(Config.TemplateName)
                ? string.Format("{0}_{1}", Config.SourceCheckIdToPush, Config.TargetWidgetId)
                : Config.TemplateName;
        }
    }
}